Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@slack/web-api
Advanced tools
The @slack/web-api npm package is a JavaScript client for Slack's Web API. It provides an easy-to-use interface for interacting with various Slack API methods, allowing developers to integrate their applications with Slack services. With this package, developers can send messages, manage channels, handle users, and more, all within their Node.js applications.
Sending Messages
This feature allows you to send messages to channels or users in Slack. The code sample demonstrates how to use the `chat.postMessage` method to send a 'Hello world!' message to a specified channel.
const { WebClient } = require('@slack/web-api');
const web = new WebClient(process.env.SLACK_TOKEN);
async function sendMessage(channel, message) {
try {
const result = await web.chat.postMessage({
channel: channel,
text: message
});
console.log(result);
} catch (error) {
console.error(error);
}
}
sendMessage('C1234567890', 'Hello world!');
Managing Channels
This feature enables the creation, modification, and management of channels. The code sample shows how to create a new channel using the `conversations.create` method.
const { WebClient } = require('@slack/web-api');
const web = new WebClient(process.env.SLACK_TOKEN);
async function createChannel(name) {
try {
const result = await web.conversations.create({
name: name
});
console.log(result);
} catch (error) {
console.error(error);
}
}
createChannel('new-channel');
Handling Users
This feature deals with user information and actions, such as listing users, getting user info, and updating user profiles. The code sample illustrates how to list all users in a Slack workspace using the `users.list` method.
const { WebClient } = require('@slack/web-api');
const web = new WebClient(process.env.SLACK_TOKEN);
async function listUsers() {
try {
const result = await web.users.list();
console.log(result.members);
} catch (error) {
console.error(error);
}
}
listUsers();
Botkit is a toolkit for making bot applications. While it also supports Slack, it is more of a comprehensive bot framework that can work with multiple messaging platforms. It provides more features for conversational UIs and dialog management compared to @slack/web-api, which is more focused on Slack's Web API.
slackbots is another npm package that provides an interface to the Slack API. It is similar to @slack/web-api but has a different API design and may not be as up-to-date with the latest Slack API features. It's a simpler alternative for sending messages and handling events.
hubot-slack is a Slack adapter for GitHub's Hubot, a scriptable chat bot. This package allows Hubot to run on Slack. It differs from @slack/web-api as it is part of a larger bot framework and is not solely a client for the Slack Web API.
The @slack/web-api
package contains a simple, convenient, and configurable HTTP client for making requests to Slack's
Web API. Use it in your app to call any of the over 130
methods, and let it handle formatting, queuing, retrying, pagination, and more.
This package supports Node v14 and higher. It's highly recommended to use the latest LTS version of node, and the documentation is written using syntax and features from that version.
This package also has experimental support for Deno v1.15.2 and higher, though not all features are supported at this time.
$ npm install @slack/web-api
import { WebClient } from 'https://deno.land/x/slack_web_api/mod.js';
These examples show the most common features of the WebClient
. You'll find even more extensive documentation on the
package's website.
The package exports a WebClient
class. All you need to do is instantiate it, and you're ready to go. You'll typically
initialize it with a token
, so that you don't have to provide the token each time you call a method. A token usually
begins with xoxb
or xoxp
. You get them from each workspace an app is installed onto. The app configuration pages
help you get your first token for your development workspace.
const { WebClient } = require('@slack/web-api');
// Read a token from the environment variables
const token = process.env.SLACK_TOKEN;
// Initialize
const web = new WebClient(token);
Alternatively, you can create a client without a token, and use it with multiple workspaces as long as you supply a
token
when you call a method.
const { WebClient } = require('@slack/web-api');
// Initialize a single instance for the whole app
const web = new WebClient();
// Find a token in storage (database) before making an API method call
(async () => {
// Some fictitious database
const token = await db.findTokenByTeam(teamId, enterpriseId)
// Call the method
const result = web.auth.test({ token });
})();
The client instance has a named method for each of the public methods in the Web API. The most popular one is
called chat.postMessage
, and it's used to send a message to a conversation. For every method, you pass arguments as
properties of an options object. This helps with the readability of your code since every argument has a name. All
named methods return a Promise
which resolves with the response data or rejects with an error.
// Given some known conversation ID (representing a public channel, private channel, DM or group DM)
const conversationId = '...';
(async () => {
// Post a message to the channel, and await the result.
// Find more arguments and details of the response: https://api.slack.com/methods/chat.postMessage
const result = await web.chat.postMessage({
text: 'Hello world!',
channel: conversationId,
});
// The result contains an identifier for the message, `ts`.
console.log(`Successfully send message ${result.ts} in conversation ${conversationId}`);
})();
Tip: If you're using an editor that supports TypeScript, even if you're not using TypeScript to write your code, you'll get hints for all the arguments each method supports. This helps you save time by reducing the number of times you need to pop out to a webpage to check the reference. There's more information about using TypeScript with this package in the documentation website.
Tip: Use the Block Kit Builder for a playground where you can prototype your message's look and feel.
If you want to provide the method name as a string so that you can decide which method to call dynamically or to call
a method that might not be available in your version of the client, use the WebClient.apiCall(methodName, [options])
method. The API method call above can also be written as follows:
const conversationId = '...';
(async () => {
// Using apiCall() allows the app to call any method and to do it programmatically
const response = await web.apiCall('chat.postMessage', {
text: 'Hello world!',
channel: conversationId,
});
})();
Errors can happen for many reasons: maybe the token doesn't have the proper scopes to
call a method, maybe its been revoked by a user, or maybe you just used a bad argument. In these cases, the returned
Promise
will reject with an Error
. You should catch the error and use the information it contains to decide how your
app can proceed.
Each error contains a code
property, which you can check against the ErrorCode
export to understand the kind of
error you're dealing with. For example, when Slack responds to your app with an error, that is an
ErrorCode.PlatformError
. These types of errors provide Slack's response body as the data
property.
// Import ErrorCode from the package
const { WebClient, ErrorCode } = require('@slack/web-api');
(async () => {
try {
// This method call should fail because we're giving it a bogus user ID to lookup.
const response = await web.users.info({ user: '...' });
} catch (error) {
// Check the code property, and when its a PlatformError, log the whole response.
if (error.code === ErrorCode.PlatformError) {
console.log(error.data);
} else {
// Some other error, oh no!
console.log('Well, that was unexpected.');
}
}
})();
There are a few more types of errors that you might encounter, each with one of these code
s:
ErrorCode.RequestError
: A request could not be sent. A common reason for this is that your network connection is
not available, or api.slack.com
could not be reached. This error has an original
property with more details.
ErrorCode.RateLimitedError
: The Web API cannot fulfill the API method call because your app has made too many
requests too quickly. This error has a retryAfter
property with the number of seconds you should wait before trying
again. See the documentation on rate limit handling to
understand how the client will automatically deal with these problems for you.
ErrorCode.HTTPError
: The HTTP response contained an unfamiliar status code. The Web API only responds with 200
(yes, even for errors) or 429
(rate limiting). If you receive this error, it's likely due to a problem with a proxy,
a custom TLS configuration, or a custom API URL. This error has the statusCode
, statusMessage
, headers
, and
body
properties containing more details.
Many of the Web API's methods return
lists of objects, and are known to be cursor-paginated. The result of calling these methods will contain a part of
the list, or a page, and also provide you with information on how to continue to the next page on a subsequent API call.
Instead of calling many times manually, the WebClient
can manage to get each page, allowing you to determine when to
stop, and help you process the results.
The process of retrieving multiple pages from Slack's API can be described as asynchronous iteration, which means
you're processing items in a collection, but getting each item is an asynchronous operation. Fortunately, JavaScript
has this concept built-in, and in newer versions of the language there's a syntax to make it even simpler:
for await...of
.
(async () => {
let result;
// Async iteration is similar to a simple for loop.
// Use only the first two parameters to get an async iterator.
for await (const page of web.paginate('something.list', { name: 'value' })) {
// You can inspect each page, find your result, and stop the loop with a `break` statement
if (containsTheThing(page.something)) {
result = page.something.thing;
break;
}
}
});
The for await...of
syntax is available in Node v10.0.0 and above. If you're using an older version of Node, see
functional iteration below.
The .paginate()
method can accept up to two additional parameters. The third parameter, stopFn
, is a function that
is called once for each page of the result, and should return true
when the app no longer needs to get another page.
The fourth parameter is reducerFn
, which is a function that gets called once for each page of the result, but can
be used to aggregate a result. The value it returns is used to call it the next time as the accumulator
. The first
time it gets called, the accumulator
is undefined.
(async () => {
// The first two parameters are the method name and the options object.
const done = await web.paginate('something.list', { name: 'value' },
// The third is a function that receives each page and should return true when the next page isn't needed.
(page) => { /* ... */ },
// The fourth is a reducer function, similar to the callback parameter of Array.prototype.reduce().
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
// The accumulator is initialized to undefined.
(accumulator, page, index) => { /* ... */ },
);
});
The returned value is a Promise
, but what it resolves to depends on whether or not you include the fourth (optional)
parameter. If you don't include it, the resolved value is always undefined
. In this case, its used for control flow
purposes (resuming the rest of your program), and the function in the third parameter is used to capture a result. If
you do include the fourth parameter, then the resolved value is the value of the accumulator
. This is a familiar
pattern for people that use functional programming.
The WebClient
will log interesting information to the console by default. You can use the logLevel
to decide how
much information, or how interesting the information needs to be, in order for it to be output. There are a few possible
log levels, which you can find in the LogLevel
export. By default, the value is set to LogLevel.INFO
. While you're
in development, its sometimes helpful to set this to the most verbose: LogLevel.DEBUG
.
// Import LogLevel from the package
const { WebClient, LogLevel } = require('@slack/web-api');
// Log level is one of the options you can set in the constructor
const web = new WebClient(token, {
logLevel: LogLevel.DEBUG,
});
All the log levels, in order of most to least information, are: DEBUG
, INFO
, WARN
, and ERROR
.
You can also choose to have logs sent to a custom logger using the logger
option. A custom logger needs to implement
specific methods (known as the Logger
interface):
Method | Parameters | Return type |
---|---|---|
setLevel() | level: LogLevel | void |
setName() | name: string | void |
debug() | ...msgs: any[] | void |
info() | ...msgs: any[] | void |
warn() | ...msgs: any[] | void |
error() | ...msgs: any[] | void |
A very simple custom logger might ignore the name and level, and write all messages to a file.
const { createWriteStream } = require('fs');
const logWritable = createWriteStream('/var/my_log_file'); // Not shown: close this stream
const web = new WebClient(token, {
// Creating a logger as a literal object. It's more likely that you'd create a class.
logger: {
debug(...msgs): { logWritable.write('debug: ' + JSON.stringify(msgs)); },
info(...msgs): { logWritable.write('info: ' + JSON.stringify(msgs)); },
warn(...msgs): { logWritable.write('warn: ' + JSON.stringify(msgs)); },
error(...msgs): { logWritable.write('error: ' + JSON.stringify(msgs)); },
setLevel(): { },
setName(): { },
},
});
In production systems, you want your app to be resilient to short hiccups and temporary outages. Solving for this
problem usually involves building a queuing system that handles retrying failed tasks. The WebClient
comes with this
queuing system out of the box, and it's on by default! The client will retry a failed API method call up to 10 times,
spaced out over about 30 minutes. If the request doesn't succeed within that time, then the returned Promise
will reject.
You can observe each of the retries in your logs by setting the log level to DEBUG. Try running the
following code with your network disconnected, and then re-connect after you see a couple of log messages:
const { WebClient, LogLevel } = require('@slack/web-api');
const web = new WebClient('bogus token');
(async () => {
await web.auth.test();
console.log('Done!');
})();
Shortly after re-connecting your network, you should see the Done!
message. Did you notice the program doesn't use a
valid token? The client is doing something clever and helpful here. It knows the difference between an error such as not
being able to reach api.slack.com
and an error in the response from Slack about an invalid token. The former is
something that can be resolved with a retry, so it was retried. The invalid token error means that the call isn't going
to succeed until your app does something differently, so it stops attempting retries.
You might not think 10 reties in 30 minutes is a good policy for your app. No problem, you can set the retryConfig
to
one that works better for you. The retryPolicies
export contains a few well known options, and you can always write
your own.
const { WebClient, retryPolicies } = require('@slack/web-api');
const web = new WebClient(token, {
retryConfig: retryPolicies.fiveRetriesInFiveMinutes,
});
Here are some other values that you might want to use for retryConfig
:
retryConfig | Description |
---|---|
retryPolicies.tenRetriesInAboutThirtyMinutes | (default) |
retryPolicies.fiveRetriesInFiveMinutes | Five attempts in five minutes |
retryPolicies.rapidRetryPolicy | Used to keep tests running fast |
{ retries: 0 } | No retries (other options) |
Note: If an API call results in a rate limit being exceeded, you might still notice the client automatically
retrying the API call. If you'd like to opt out of that behavior, set the rejectRateLimitedCalls
option to true
.
The documentation website has information about these additional features of
the WebClient
:
Buffer
or a ReadableStream
.If you get stuck, we're here to help. The following are the best ways to get assistance working through your issue:
developers@slack.com
FAQs
Official library for using the Slack Platform's Web API
The npm package @slack/web-api receives a total of 1,453,856 weekly downloads. As such, @slack/web-api popularity was classified as popular.
We found that @slack/web-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 13 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.